package com.neoden.notes.data;

import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

public class NotesProvider extends ContentProvider {
	private static final String TAG = NotesProvider.class.getName();
	
	private DBHelper mDBHelper;
	
	private static final String AUTHORITY = "com.neoden.notes.provider";
	
	private static final UriMatcher sURIMatcher = new UriMatcher(
	        UriMatcher.NO_MATCH);
	
	public static final int NOTES = 100;
	public static final int NOTE_ID = 110;
	public static final int LISTS = 200;
	public static final int LIST_ID = 210;
	
	public static final class Notes {
		public static final String TABLE_NAME = "notes";
		public static final String BASE_PATH = TABLE_NAME;
		public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);

		public static final String CONTENT_ITEM_TYPE = 
				ContentResolver.CURSOR_ITEM_BASE_TYPE + "/vnd.com.neoden.notes.data.note";
		public static final String CONTENT_TYPE = 
				ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd.com.neoden.notes.data.note";
		
		public static final String COLUMN_ID = "_id";
		public static final String COLUMN_TITLE = "title";
		public static final String COLUMN_NOTE = "note";
		public static final String COLUMN_CREATED_TS = "createdTS";
		public static final String COLUMN_MODIFIED_TS = "modifiedTS";
		public static final String COLUMN_LIST_ID = "listId";
		
		public static final String SQL_CREATE_TABLE = "CREATE TABLE "
				+ TABLE_NAME + " ("
				+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 
				+ COLUMN_TITLE + " TEXT, " 
				+ COLUMN_NOTE + " TEXT, "
				+ COLUMN_CREATED_TS + " LONG, "
				+ COLUMN_MODIFIED_TS + " LONG, "
				+ COLUMN_LIST_ID + " INTEGER DEFAULT 0"
				+");";
		
		public static final String[] PROJECTION_FULL = {
			COLUMN_ID,
			COLUMN_TITLE,
			COLUMN_NOTE,
			COLUMN_CREATED_TS,
			COLUMN_MODIFIED_TS,
			COLUMN_LIST_ID
		};
	}
	
	public static final class Lists {
		public static final String TABLE_NAME = "lists";
		public static final String BASE_PATH = TABLE_NAME;
		public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);
		
		public static final String CONTENT_ITEM_TYPE = 
				ContentResolver.CURSOR_ITEM_BASE_TYPE + "/vnd.com.neoden.notes.data.list";
		public static final String CONTENT_TYPE = 
				ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd.com.neoden.notes.data.list";

		public static final String COLUMN_ID = "_id";
		public static final String COLUMN_TITLE = "title";

		public static final String SQL_CREATE_TABLE = "CREATE TABLE "
				+ TABLE_NAME + " ("
				+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 
				+ COLUMN_TITLE + " TEXT" 
				+ ");";
		
		public static final String[] PROJECTION_FULL = {
			COLUMN_ID,
			COLUMN_TITLE
		};
	}
	
	static {
	    sURIMatcher.addURI(AUTHORITY, Notes.BASE_PATH, NOTES);
	    sURIMatcher.addURI(AUTHORITY, Notes.BASE_PATH + "/#", NOTE_ID);
	    sURIMatcher.addURI(AUTHORITY, Lists.BASE_PATH, LISTS);
	    sURIMatcher.addURI(AUTHORITY, Lists.BASE_PATH + "/#", LIST_ID);
	}	

	private class DBHelper extends SQLiteOpenHelper {
		private static final String DATABASE_NAME = "notes.db";
		private static final int DATABASE_VERSION = 2;	
		
		public DBHelper(Context context) {
			super(context, DATABASE_NAME, null, DATABASE_VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase database) {
			database.execSQL(Notes.SQL_CREATE_TABLE);
			database.execSQL(Lists.SQL_CREATE_TABLE);
			
			// insert default list item
			ContentValues values = new ContentValues();
			values.put(Lists.COLUMN_TITLE, "");
			database.insert(Lists.TABLE_NAME, null, values);
		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
			Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
							+ newVersion);

			// continuously upgrade from oldVersion to newVersion one by one
			for (int i = oldVersion + 1; i <= newVersion; i++) {
				if (upgradeFromPrevious(db, i) == false) {
					Log.e(TAG, "Upgrade from " + oldVersion 
							+ " to " + newVersion + " failed");
					break;
				}
			}
		}
		
		private boolean upgradeFromPrevious(SQLiteDatabase db, int newVersion) {
			switch (newVersion) {
			case 2:
				// create lists table
				db.execSQL(Lists.SQL_CREATE_TABLE);
				
				// add listId column to notes table
				db.execSQL("ALTER TABLE " + Notes.TABLE_NAME + " ADD COLUMN listId integer DEFAULT 0;");
				
				// insert default list item
				ContentValues values = new ContentValues();
				values.put(Lists.COLUMN_TITLE, "");
				db.insert(Lists.TABLE_NAME, null, values);
				
				// update listId to link with default list item
				values = new ContentValues();
				values.put(Notes.COLUMN_LIST_ID, 1);
				db.update(Notes.TABLE_NAME, values, null, null);
				
				return true;
			default:
				Log.w(TAG, "Don't know how to upgrade to version " + newVersion);
				return false;
			}
		}
	}
	
    public NotesProvider() {
    }
   
    @Override
    public boolean onCreate() {
    	mDBHelper = new DBHelper(getContext());
    	
    	return true;
    }

    @Override
    public String getType(Uri uri) {
        int uriType = sURIMatcher.match(uri);
        switch (uriType) {
        case NOTES:
            return Notes.CONTENT_TYPE;
        case NOTE_ID:
            return Notes.CONTENT_ITEM_TYPE;
        case LISTS:
            return Lists.CONTENT_TYPE;
        case LIST_ID:
            return Lists.CONTENT_ITEM_TYPE;
        default:
            return null;
        }    
    }
    
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
	    int uriType = sURIMatcher.match(uri);
	    SQLiteDatabase sqlDB = mDBHelper.getWritableDatabase();
	    int rowsAffected = 0;
	    String id;
	    
	    switch (uriType) {
	    case NOTES:
	        rowsAffected = sqlDB.delete(Notes.TABLE_NAME,
	                selection, selectionArgs);
	        break;
	    case NOTE_ID:
	        id = uri.getLastPathSegment();
	        if (TextUtils.isEmpty(selection)) {
	            rowsAffected = sqlDB.delete(Notes.TABLE_NAME,
	            		Notes.COLUMN_ID + "=" + id, null);
	        } else {
	            rowsAffected = sqlDB.delete(Notes.TABLE_NAME,
	                    selection + " and " + Notes.COLUMN_ID + "=" + id,
	                    selectionArgs);
	        }
	        break;
	    case LISTS:
	    	// TODO: delete lists by selection
	    	throw new UnsupportedOperationException("Not yet implemented");
	        /*rowsAffected = sqlDB.delete(DBContract.Lists.TABLE_NAME,
	                selection, selectionArgs);*/
	    case LIST_ID:
	        id = uri.getLastPathSegment();
	        if (TextUtils.isEmpty(selection)) {
	        	sqlDB.beginTransaction();
	        	try {
		        	// delete associated notes first
		        	sqlDB.delete(Notes.TABLE_NAME,
		        			Notes.COLUMN_LIST_ID + "=" + id, null);
	        		
		        	// delete list itself
		            rowsAffected = sqlDB.delete(Lists.TABLE_NAME,
		            		Lists.COLUMN_ID + "=" + id, null);
		            
		            sqlDB.setTransactionSuccessful();
	        	}
	        	finally {
	        		sqlDB.endTransaction();
	        	}
	        } else {
		    	// TODO: delete list by id and selection
		    	throw new UnsupportedOperationException("Not yet implemented");
	        	
	            /*rowsAffected = sqlDB.delete(DBContract.Lists.TABLE_NAME,
	                    selection + " and " + DBContract.Lists.COLUMN_ID + "=" + id,
	                    selectionArgs);*/
	        }
	        break;	        
	    default:
	        throw new IllegalArgumentException("Unknown or Invalid URI " + uri);
	    }
	    
	    getContext().getContentResolver().notifyChange(uri, null);
	    
	    return rowsAffected;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase sqlDB = mDBHelper.getWritableDatabase();
        int uriType = sURIMatcher.match(uri);

        String table = "";
        
        switch (uriType) {
        case NOTES:
        	table = Notes.TABLE_NAME;
        	break;
        case LISTS:
        	table = Lists.TABLE_NAME;
        	break;
        default:
        	throw new IllegalArgumentException("Unknown or Invalid URI " + uri);
        }        

        try {
            long newID = sqlDB.insertOrThrow(table, null, values);
            if (newID > 0) {
                Uri newUri = ContentUris.withAppendedId(uri, newID);
                getContext().getContentResolver().notifyChange(uri, null);
                return newUri;
            } else {
                throw new SQLException("Failed to insert row into " + uri);
            }
        } catch (SQLiteConstraintException e) {
            Log.i(NotesProvider.class.getName(), "Ignoring constraint failure.");
        }
        return null;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) 
    {
	    SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
	    int uriType = sURIMatcher.match(uri);
	    
	    switch (uriType) {
	    case NOTE_ID:
	        queryBuilder.appendWhere(Notes.COLUMN_ID + "="
	                + uri.getLastPathSegment());
	        // no break
	    case NOTES:
	    	queryBuilder.setTables(Notes.TABLE_NAME);
	    	break;
	    case LIST_ID:
	        queryBuilder.appendWhere(Lists.COLUMN_ID + "="
	                + uri.getLastPathSegment());
	        // no break
	    case LISTS:
	    	queryBuilder.setTables(Lists.TABLE_NAME);
	    	break;
	    default:
	        throw new IllegalArgumentException("Unknown URI");
	    }
	 
	    Cursor cursor = queryBuilder.query(mDBHelper.getReadableDatabase(),
	            projection, selection, selectionArgs, null, null, sortOrder);
	    cursor.setNotificationUri(getContext().getContentResolver(), uri);
	    
	    return cursor;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) 
    {
        SQLiteDatabase sqlDB = mDBHelper.getWritableDatabase();
        int uriType = sURIMatcher.match(uri);
        int rowsAffected;

        String table = "";
        String id = "";
        String columnIdName = "";

        StringBuilder modSelection = new StringBuilder();
        
    	if (!TextUtils.isEmpty(selection)) {
            modSelection.append(" AND " + selection);
        }
    	
        if (uriType == NOTE_ID || uriType == NOTES) {
        	table = Notes.TABLE_NAME;
        } 
        else if (uriType == LIST_ID || uriType == LISTS) {
        	table = Lists.TABLE_NAME;
        }
        else {
        	throw new IllegalArgumentException("Unknown URI");
        }
        
        if (uriType == NOTE_ID || uriType == LIST_ID) {
        	id = uri.getLastPathSegment();
            switch (uriType) {
            case NOTE_ID:
            	columnIdName = Notes.COLUMN_ID;
            	break;
            case LIST_ID:
            	columnIdName = Lists.COLUMN_ID;
            	break;
            }
        	modSelection.append(columnIdName + "=" + id);
        }
        
        rowsAffected = sqlDB.update(table,
                values, modSelection.toString(), selectionArgs);
        
        getContext().getContentResolver().notifyChange(uri, null);
        return rowsAffected;
    }
}
